package main

import (
	"fmt"
	"log"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"strings"
	"sync"
	"time"
)

// 配置常量（可通过环境变量覆盖）
const (
	defaultUsername = "admin"               // 默认用户名（环境变量优先）
	defaultPassword = "your_strong_password" // 默认密码（环境变量优先）
	maxLogs         = 1000                  // 日志最大保留条数
	targetURLStr    = "https://enka.network" // 目标服务器 URL（字符串常量）
)

var (
	logs     []LogEntry      // 存储请求日志的全局切片
	logMutex sync.Mutex      // 保护日志的互斥锁
	targetURL *url.URL       // 解析后的目标服务器 URL
	username string          // 认证用户名（从环境变量获取）
	password string          // 认证密码（从环境变量获取）
)

// LogEntry 日志条目结构体
type LogEntry struct {
	IP        string    // 客户端 IP
	Method    string    // 请求方法
	Path      string    // 请求路径
	Timestamp time.Time // 请求时间
}

func main() {
	// 从环境变量获取认证信息（优先使用运行时传递的值）
	username = os.Getenv("LOG_USERNAME")
	password = os.Getenv("LOG_PASSWORD")

	// 如果环境变量未设置，使用默认值（生产环境建议删除默认值）
	if username == "" {
		username = defaultUsername
		log.Printf("警告：未设置 LOG_USERNAME，使用默认值：%s", username)
	}
	if password == "" {
		password = defaultPassword
		log.Printf("警告：未设置 LOG_PASSWORD，使用默认值：%s", password)
	}

	// 解析目标服务器 URL（从字符串常量解析）
	var err error
	targetURL, err = url.Parse(targetURLStr)
	if err != nil {
		log.Fatalf("解析目标 URL 失败: %v", err)
	}

	// 创建反向代理并优化 Transport（超时配置）
	proxy := httputil.NewSingleHostReverseProxy(targetURL)
	proxy.Transport = &http.Transport{
		DialContext: (&net.Dialer{
			Timeout:   10 * time.Second, // 连接目标服务器超时
			KeepAlive: 30 * time.Second, // 长连接保持超时
		}).DialContext,
		TLSHandshakeTimeout:   10 * time.Second, // TLS 握手超时
		ResponseHeaderTimeout: 15 * time.Second, // 等待响应头超时
		MaxIdleConns:          100,              // 最大空闲连接数
		IdleConnTimeout:       90 * time.Second, // 空闲连接超时
	}

	// 注册主代理处理函数（所有请求先到这里）
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		// 记录请求日志（IP、方法、路径、时间）
		clientIP := getClientIP(r)
		entry := LogEntry{
			IP:        clientIP,
			Method:    r.Method,
			Path:      r.URL.Path,
			Timestamp: time.Now(),
		}

		// 加锁写入日志（限制最大数量）
		logMutex.Lock()
		logs = append(logs, entry)
		if len(logs) > maxLogs {
			logs = logs[1:] // 超过限制时删除旧日志
		}
		logMutex.Unlock()

		// 控制台输出日志（可选）
		log.Printf("收到请求 | IP: %-15s 方法: %-6s 路径: %s", clientIP, r.Method, r.URL.Path)

		// 转发请求到目标服务器（修改 Host 头）
		r.Host = targetURL.Host
		proxy.ServeHTTP(w, r)
	})

	// 注册受密码保护的日志查看接口
	http.HandleFunc("/log", authMiddleware(logHandler))

	// 启动代理服务
	log.Println("反向代理服务启动，监听端口 :7860")
	log.Println("日志查看地址：http://localhost:7860/log （需输入用户名和密码）")
	if err := http.ListenAndServe(":7860", nil); err != nil {
		log.Fatalf("启动失败: %v", err)
	}
}

// getClientIP 从请求中提取真实客户端 IP（支持代理场景）
func getClientIP(r *http.Request) string {
	// 优先从 X-Forwarded-For 头获取（可能包含多个 IP，取第一个）
	if xff := r.Header.Get("X-Forwarded-For"); xff != "" {
		ips := strings.Split(xff, ",")
		for _, ip := range ips {
			trimmed := strings.TrimSpace(ip)
			if trimmed != "" {
				return trimmed
			}
		}
	}

	// 其次从 X-Real-IP 头获取（常见于 Nginx 代理）
	if xri := r.Header.Get("X-Real-IP"); xri != "" {
		return strings.TrimSpace(xri)
	}

	// 最后从 RemoteAddr 解析（无代理时的客户端 IP）
	ip, _, _ := net.SplitHostPort(r.RemoteAddr)
	return ip
}

// logHandler 处理日志查看请求（需先通过认证）
func logHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/html; charset=utf-8")

	// 生成 HTML 页面
	fmt.Fprint(w, `
	<!DOCTYPE html>
	<html>
	<head>
		<title>代理请求日志</title>
		<style>
			body { font-family: Arial, sans-serif; margin: 20px; }
			h1 { color: #2c3e50; }
			table { width: 100%; border-collapse: collapse; margin-top: 20px; }
			th, td { padding: 12px; text-align: left; border-bottom: 1px solid #ddd; }
			th { background-color: #f8f9fa; }
			tr:hover { background-color: #f1f1f1; }
		</style>
	</head>
	<body>
		<h1>最近请求日志（最多1000条）</h1>
		<table>
			<tr>
				<th>时间</th>
				<th>客户端IP</th>
				<th>请求方法</th>
				<th>请求路径</th>
			</tr>
	`)

	// 倒序遍历日志并输出（最新日志在前）
	logMutex.Lock()
	defer logMutex.Unlock()
	for i := len(logs) - 1; i >= 0; i-- {
		entry := logs[i]
		fmt.Fprintf(w, `
			<tr>
				<td>%s</td>
				<td>%s</td>
				<td>%s</td>
				<td>%s</td>
			</tr>
		`, 
			entry.Timestamp.Format("2006-01-02 15:04:05"), // 格式化时间
			entry.IP,
			entry.Method,
			entry.Path,
		)
	}

	fmt.Fprint(w, `
		</table>
	</body>
	</html>
	`)
}

// authMiddleware 认证中间件（保护 /log 路径）
func authMiddleware(next http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		// 从请求头获取 Basic Auth 信息
		reqUser, reqPass, ok := r.BasicAuth()
		if !ok {
			// 未提供认证信息：返回 401 并提示
			w.Header().Set("WWW-Authenticate", `Basic realm="Restricted Area"`)
			http.Error(w, "未授权访问：请输入用户名和密码", http.StatusUnauthorized)
			return
		}

		// 校验用户名和密码
		if reqUser != username || reqPass != password {
			http.Error(w, "认证失败：用户名或密码错误", http.StatusUnauthorized)
			return
		}

		// 认证通过：调用日志处理函数
		next(w, r)
	}
}